package Controller.Actions;

import Controller.*;
import Database.CommonOperations;
import Database.DbConstants;
import Database.Keyword;
import Database.KeywordHashTable;
import Database.KeywordVector;
import Database.NamedMatrix;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.config.Entry;

import java.net.*;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class TrainAction implements Action {
    
    public TrainAction() {
    }
    
    public void perform(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
    {
        response.setContentType("text/html");
        response.setHeader("Cache-Control", "no-cache");
        
        ///retrieve query string...
        String query = request.getParameter("query");
        
        ///if null, go to training start page...
        if(query == null)
            dispatchStartTrainingPage(request, response);
        
        ///do training and display outcome...
        doTraining(request, response, query);
    }
    
    private void dispatchStartTrainingPage(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
    {        
        ///open and close connection so we recreate the database if it does not exist...
        ObjectContainer db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        db.close();
        db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        
        ///retrieve relevant data from the database...
        NamedMatrix freqMM = null;
        KeywordHashTable keywords = null;
        try
        {            
            ///get the keyword's hash table...
            keywords = CommonOperations.getKeywords(db);
            
            ///if there was no hashtable...
            if(keywords == null)
                keywords = new KeywordHashTable();

            ///get the fractMM matrix...
            freqMM = CommonOperations.getFreqMM(db);
            
            ///if there was no freqMM...
            if(freqMM == null)
                freqMM = new NamedMatrix(DbConstants.FREQMM);
        }
        catch(Exception e)
        {            
            ///go to error page...
            response.sendRedirect("/proyecto2/error?error=Error accessing the database (dispatchStartTrainingPage).");
            return;
        }
        finally
        {
            db.close();
        }
        
        ///convert pairs <keyword, integer> to an ordered vector...
        Set keywordSet = keywords.entrySet();
        Keyword[] k = new Keyword[freqMM.getRowDimension()];
        
        Iterator<java.util.Map.Entry> it = keywordSet.iterator();
        java.util.Map.Entry entry = null;
        
        while(it.hasNext())
        {
            entry = it.next();
            k[keywords.get((Keyword) entry.getKey()).intValue()] = (Keyword) entry.getKey();
        }
        
        ///send attributes...
        request.setAttribute("freqMM", freqMM);
        request.setAttribute("keywords", k);

        ///dispatch...
        RequestDispatcher rd = request.getRequestDispatcher("/train/train.jsp");
        rd.forward(request, response);
    }
    
    private void doTraining(HttpServletRequest request, HttpServletResponse response, String query)
    throws ServletException, IOException
    {        
        ///split query string...
        String[] queryKeywords = query.split(" ");
        
        ///apply filters to the query string...
        Common.Filters.Filter f = new Common.Filters.Filter();
        for(int i = 0; i < queryKeywords.length; i++)
            queryKeywords[i] = f.Filtering(queryKeywords[i]);
        
        ///open and close connection so we recreate the database if it does not exist...
        ObjectContainer db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        db.close();
        db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        
        ///retrieve relevant data from the database...
        NamedMatrix freqMM = null;
        KeywordHashTable keywords = null;
        try
        {            
            ///get the keyword's hash table...
            keywords = CommonOperations.getKeywords(db);
            
            ///if there was no hashtable...
            if(keywords == null)
                keywords = new KeywordHashTable();

            ///get the fractMM matrix...
            freqMM = CommonOperations.getFreqMM(db);
            
            ///if there was no freqMM...
            if(freqMM == null)
                freqMM = new NamedMatrix(DbConstants.FREQMM);
        
            ///check which keywords already exist in the freqMM
            ///and which not (and add them)...
            int newKeywordsCount = 0;
            Keyword k = null;
            for(int i = 0; i < queryKeywords.length; i++)
                if( queryKeywords[i] != null && ! keywords.containsKey(k = new Keyword(queryKeywords[i])))
                    keywords.put(k, new Integer(freqMM.getRowDimension() + (newKeywordsCount++)));
            
            ///create a vectorial representation of the
            ///query in a new column in freqMM...
            NamedMatrix newFreqMM = new NamedMatrix(
                freqMM.getRowDimension() + newKeywordsCount,
                freqMM.getColumnDimension() + 1,
                freqMM.getName()
                );
            newFreqMM.setMatrix(   0, freqMM.getRowDimension()-1,
                                    0, freqMM.getColumnDimension()-1,
                                    freqMM);
            for(int i = 0; i < queryKeywords.length; i++)        
                if(queryKeywords[i] != null) newFreqMM.set(keywords.get(new Keyword(queryKeywords[i])).intValue(), newFreqMM.getColumnDimension() - 1, 1);
            
            
            ///if no new words where added, check whether the query already existed...
            if(newKeywordsCount == 0 && queryExisted(newFreqMM))
            {
                ///go to error page...
                response.sendRedirect("/proyecto2/error/index.jsp?error=That training query is already in the system's database.");
                return;
            }   
            
            ///replace freqMM with the newfreqMM matrix...
            freqMM.setArray(newFreqMM.getArrayCopy());

            ///store the keywords object...
            db.set(keywords);
            
            ///store the freqMM matrix...
            db.set(freqMM);
        }
        catch(Exception e)
        {   
            ///go to error page...
            response.sendRedirect("/proyecto2/error/index.jsp?error=Error performing training (doTraining).");
            return;
        }
        finally
        {
            db.close();
        }
        
        ///dispatch to success page...
        RequestDispatcher rd = request.getRequestDispatcher("/train/success.jsp");                
        rd.forward(request, response); 
    }
    
    private boolean queryExisted(NamedMatrix m)
    {
        boolean existed = true;
        for(int i = 0; i < m.getColumnDimension() - 1; i++)
        {
            existed = true;
            for(int j = 0; j < m.getRowDimension(); j++)
            {
                if(m.get(j,i) != m.get(j,m.getColumnDimension() - 1))
                {
                    existed = false;
                    break;
                }
            }
            if(existed) return true;
        }
        return false;
    }
}
